/*-------------------------------------------------------------------------
 *
 * clausesel.c
 *	  计算子句选择性相关的例程
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/optimizer/path/clausesel.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/optimizer.h"
#include "optimizer/pathnode.h"
#include "optimizer/plancat.h"
#include "statistics/statistics.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/selfuncs.h"

/*
 * 用于累积可能的范围查询
 * 子句对信息的数据结构。
 */
typedef struct RangeQueryClause
{
	struct RangeQueryClause *next;	/* 链表中的下一个元素 */
	Node	   *var;			/* 子句的共同变量 */
	bool		have_lobound;	/* 已找到低界限子句了吗？ */
	bool		have_hibound;	/* 已找到高界限子句了吗？ */
	Selectivity lobound;		/* 变量 > 某值子句的选择性 */
	Selectivity hibound;		/* 变量 < 某值子句的选择性 */
} RangeQueryClause;

static void fc_addRangeClause(RangeQueryClause **fc_rqlist, Node *fc_clause,
						   bool fc_varonleft, bool fc_isLTsel, Selectivity fc_s2);
static RelOptInfo *fc_find_single_rel_for_clauses(PlannerInfo *fc_root,
											   List *fc_clauses);
static Selectivity fc_clauselist_selectivity_or(PlannerInfo *fc_root,
											 List *fc_clauses,
											 int fc_varRelid,
											 JoinType fc_jointype,
											 SpecialJoinInfo *fc_sjinfo,
											 bool fc_use_extended_stats);

/****************************************************************************
 *		计算选择性的例程
 ****************************************************************************/

/*
 * clauselist_selectivity -
 *	  计算一个隐式与的布尔
 *	  表达式子句列表的选择性。列表可以为空，在这种情况下必须返回1.0。
 *	  列表元素可以是RestrictInfos
 *	  或裸表达式子句——前者更受欢迎，因为
 *	  它允许结果缓存。
 *
 * 请参阅clause_selectivity()了解附加参数的含义。
 *
 * 基本方法是首先应用扩展统计，在尽可能多的
 * 子句上，以捕获跨列依赖关系等。
 * 然后通过取这些
 * 选择性的乘积来估计剩余子句，但这仅在它们具有独立
 * 概率的情况下才是正确的，实际上即使它们
 * 只引用单列也通常不是独立的。所以，我们希望在可能的情况下更聪明。
 *
 * 我们还识别“范围查询”，例如“x > 34 AND x < 42”。如果子句是限制操作子句，并且其运算符
 * 具有scalarltsel或相关函数作为其
 * 限制选择性估计器，则将其识别为可能的范围查询组件。
 * 我们将引用相同变量的这种形式的子句配对。
 * 这种形式的不可配对子句将简单地
 * 按照正常方式乘入选择性乘积。但是当我们
 * 找到一对时，我们知道选择性代表了低界限和高界限在列范围内的相对
 * 位置，因此，我们可以将选择性计算为hisel +
 * losel - 1，而不是将其计算为hisel * losel。
 * （要想象这一点，看看hisel是高界限下方的范围的分数，
 * 而losel是低界限上方的分数；所以
 * hisel可以直接解释为0..1值，但我们需要将losel转换为1-losel
 * 才能将其解释为一个值。然后可用范围是1-losel到hisel。
 * 然而，这个计算双重排除了
 * 空值，因此实际上我们需要hisel + losel + null_frac - 1。）
 *
 * 如果任一选择性恰好等于DEFAULT_INEQ_SEL，我们将忘记此方程
 * 并改用DEFAULT_RANGE_INEQ_SEL。如果方程
 * 产生一个不可能的（负）结果，也同样适用。
 *
 * 一个附带的效果是我们可以识别冗余不等式，例如
 * “x < 4 AND x < 5”；只有更紧的约束会被计入。
 *
 * 当然，这一切都非常依赖于不等式
 * 选择性函数的行为；也许有一天我们可以概括这种方法。
 */
Selectivity clauselist_selectivity(PlannerInfo *fc_root,
					   List *fc_clauses,
					   int fc_varRelid,
					   JoinType fc_jointype,
					   SpecialJoinInfo *fc_sjinfo)
{
	return clauselist_selectivity_ext(fc_root, fc_clauses, fc_varRelid,
									  fc_jointype, fc_sjinfo, true);
}

/*
 * clauselist_selectivity_ext -
 *	  clauselist_selectivity()的扩展版本。如果"use_extended_stats"
 *	  为false，则将忽略所有扩展统计，仅使用每列
 *	  统计。
 */
Selectivity clauselist_selectivity_ext(PlannerInfo *fc_root,
						   List *fc_clauses,
						   int fc_varRelid,
						   JoinType fc_jointype,
						   SpecialJoinInfo *fc_sjinfo,
						   bool fc_use_extended_stats)
{
	Selectivity fc_s1 = 1.0;
	RelOptInfo *fc_rel;
	Bitmapset  *fc_estimatedclauses = NULL;
	RangeQueryClause *fc_rqlist = NULL;
	ListCell   *fc_l;
	int			fc_listidx;

	/*
	 * 如果只有一个子句，则直接调用
	 * clause_selectivity_ext()。我们下面可能做的任何事情都与此无关。
	 */
	if (list_length(fc_clauses) == 1)
		return clause_selectivity_ext(fc_root, (Node *) linitial(fc_clauses),
									  fc_varRelid, fc_jointype, fc_sjinfo,
									  fc_use_extended_stats);

	/*
	 * 确定这些子句是否引用单个关系。如果是，并且如果
	 * 它具有扩展统计，则尝试应用这些统计。
	 */
	fc_rel = fc_find_single_rel_for_clauses(fc_root, fc_clauses);
	if (fc_use_extended_stats && fc_rel && fc_rel->rtekind == RTE_RELATION && fc_rel->statlist != NIL)
	{
		/*
		 * 尽可能多地估计使用扩展统计的子句。
		 *
		 * 'estimatedclauses'用此处估计的子句的0基于列表位置
		 * 索引填充，下面应忽略此值。
		 */
		fc_s1 = statext_clauselist_selectivity(fc_root, fc_clauses, fc_varRelid,
											fc_jointype, fc_sjinfo, fc_rel,
											&fc_estimatedclauses, false);
	}

	/*
	 * 对剩余子句应用正常选择性估计。我们将小心跳过
	 * 任何已在上面被估计的子句。
	 *
	 * 任何看起来不像潜在范围查询子句的内容都将
	 * 乘入s1并被遗忘。任何看起来像的内容都会插入到
	 * rqlist条目中。
	 */
	fc_listidx = -1;
	foreach(fc_l, fc_clauses)
	{
		Node	   *fc_clause = (Node *) lfirst(fc_l);
		RestrictInfo *fc_rinfo;
		Selectivity fc_s2;

		fc_listidx++;

		/*
		 * 如果此子句已被上述某些其他统计估计，则跳过此子句。
		 */
		if (bms_is_member(fc_listidx, fc_estimatedclauses))
			continue;

		
/* 计算此子句的选择性 */
		fc_s2 = clause_selectivity_ext(fc_root, fc_clause, fc_varRelid, fc_jointype, fc_sjinfo,
									fc_use_extended_stats);

		/*
		 * 检查是否传入了一个 RestrictInfo。
		 *
		 * 如果它是一个伪恒定的 RestrictInfo，那么 s2 要么是 1.0 要么是
		 * 0.0；只需使用它，而不是寻找范围对。
		 */
		if (IsA(fc_clause, RestrictInfo))
		{
			fc_rinfo = (RestrictInfo *) fc_clause;
			if (fc_rinfo->pseudoconstant)
			{
				fc_s1 = fc_s1 * fc_s2;
				continue;
			}
			fc_clause = (Node *) fc_rinfo->clause;
		}
		else
			fc_rinfo = NULL;

		/*
		 * 查看它是否看起来像一个一侧有伪恒定的限制子句。
		 * （任何比这更复杂的可能不会以我们所期望的简单方式运行。）
		 * 这里的大多数测试可以使用 rinfo 以更高效的方式执行，而不是不使用。
		 */
		if (is_opclause(fc_clause) && list_length(((OpExpr *) fc_clause)->args) == 2)
		{
			OpExpr	   *fc_expr = (OpExpr *) fc_clause;
			bool		fc_varonleft = true;
			bool		fc_ok;

			if (fc_rinfo)
			{
				fc_ok = (bms_membership(fc_rinfo->clause_relids) == BMS_SINGLETON) &&
					(is_pseudo_constant_clause_relids(lsecond(fc_expr->args),
													  fc_rinfo->right_relids) ||
					 (fc_varonleft = false,
					  is_pseudo_constant_clause_relids(linitial(fc_expr->args),
													   fc_rinfo->left_relids)));
			}
			else
			{
				fc_ok = (NumRelids(fc_root, fc_clause) == 1) &&
					(is_pseudo_constant_clause(lsecond(fc_expr->args)) ||
					 (fc_varonleft = false,
					  is_pseudo_constant_clause(linitial(fc_expr->args))));
			}

			if (fc_ok)
			{
				/*
				 * 如果它不是 "<"/"<="/">"/">=" 操作符，就以通用方式合并
				 * 选择性。但是如果它是正确的 oprrest，
				 * 将子句添加到 rqlist 以便稍后处理。
				 */
				switch (get_oprrest(fc_expr->opno))
				{
					case F_SCALARLTSEL:
					case F_SCALARLESEL:
						fc_addRangeClause(&fc_rqlist, fc_clause,
									   fc_varonleft, true, fc_s2);
						break;
					case F_SCALARGTSEL:
					case F_SCALARGESEL:
						fc_addRangeClause(&fc_rqlist, fc_clause,
									   fc_varonleft, false, fc_s2);
						break;
					default:
						/* 以通用方式合并选择性 */
						fc_s1 = fc_s1 * fc_s2;
						break;
				}
				continue;		/* 跳到循环底部 */
			}
		}

		/* 不是正确的形式，因此以通用方式处理。 */
		fc_s1 = fc_s1 * fc_s2;
	}

	/*
	 * 现在扫描范围查询对列表。
	 */
	while (fc_rqlist != NULL)
	{
		RangeQueryClause *fc_rqnext;

		if (fc_rqlist->have_lobound && fc_rqlist->have_hibound)
		{
			/* 成功匹配了一对范围子句 */
			Selectivity fc_s2;

			/*
			 * 与默认值完全相等可能意味着选择性函数未能处理。这
			 * 不是绝对可靠，但应该足够好。
			 */
			if (fc_rqlist->hibound == DEFAULT_INEQ_SEL ||
				fc_rqlist->lobound == DEFAULT_INEQ_SEL)
			{
				fc_s2 = DEFAULT_RANGE_INEQ_SEL;
			}
			else
			{
				fc_s2 = fc_rqlist->hibound + fc_rqlist->lobound - 1.0;

				/* 调整以避免对NULL的双重排除 */
				fc_s2 += nulltestsel(fc_root, IS_NULL, fc_rqlist->var,
								  fc_varRelid, fc_jointype, fc_sjinfo);

				/*
				 * 零或稍微负的s2应转换为小的正值；我们可能正在
				 * 处理非常紧密的范围，并由于舍入误差得到了错误的
				 * 结果。然而，如果s2非常负，那么我们可能在一边
				 * 或两边有默认的选择性估计，但由于某种原因未能
				 * 识别。
				 */
				if (fc_s2 <= 0.0)
				{
					if (fc_s2 < -0.01)
					{
						/*
						 * 没有可用数据 --- 使用一个小的默认估计，
						 * 但不是非常小。
						 */
						fc_s2 = DEFAULT_RANGE_INEQ_SEL;
					}
					else
					{
						/*
						 * 这只是舍入误差；使用一个小的正值
						 */
						fc_s2 = 1.0e-10;
					}
				}
			}
			/* 合并这一对子句的选择性 */
			fc_s1 *= fc_s2;
		}
		else
		{
			/* 只找到了一对中的一个，通用地合并它 */
			if (fc_rqlist->have_lobound)
				fc_s1 *= fc_rqlist->lobound;
			else
				fc_s1 *= fc_rqlist->hibound;
		}
		/* 释放存储并前进 */
		fc_rqnext = fc_rqlist->next;
		pfree(fc_rqlist);
		fc_rqlist = fc_rqnext;
	}

	return fc_s1;
}

/*
 * clauselist_selectivity_or -
 *	  计算隐式或运算的布尔表达式子句列表的选择性。
 *	  列表可以为空，返回0.0。
 *	  列表元素可以是RestrictInfos
 *	  或简单表达式子句 --- 前者更受欢迎，因为
 *	  它允许缓存结果。
 *
 * 参见clause_selectivity()获取附加参数的含义。
 *
 * 基本方法是首先对尽可能多的子句应用扩展统计
 * 以捕捉跨列依赖等。然后其余子句被估计为
 * 独立的。
 */
static Selectivity fc_clauselist_selectivity_or(PlannerInfo *fc_root,
						  List *fc_clauses,
						  int fc_varRelid,
						  JoinType fc_jointype,
						  SpecialJoinInfo *fc_sjinfo,
						  bool fc_use_extended_stats)
{
	Selectivity fc_s1 = 0.0;
	RelOptInfo *fc_rel;
	Bitmapset  *fc_estimatedclauses = NULL;
	ListCell   *fc_lc;
	int			fc_listidx;

	/*
	 * 确定这些子句是否引用单个关系。如果是，并且如果
	 * 它具有扩展统计，则尝试应用这些统计。
	 */
	fc_rel = fc_find_single_rel_for_clauses(fc_root, fc_clauses);
	if (fc_use_extended_stats && fc_rel && fc_rel->rtekind == RTE_RELATION && fc_rel->statlist != NIL)
	{
		/*
		 * 尽可能多地估计使用扩展统计的子句。
		 *
		 * 'estimatedclauses'用此处估计的子句的0基于列表位置
		 * 索引填充，下面应忽略此值。
		 */
		fc_s1 = statext_clauselist_selectivity(fc_root, fc_clauses, fc_varRelid,
											fc_jointype, fc_sjinfo, fc_rel,
											&fc_estimatedclauses, true);
	}

	/*
	 * 将剩余的子句估计为独立的。
	 *
	 * 对于一个或子句，选择性通过s1+s2 - s1*s2计算
	 * 以考虑被选择的元组集合的可能重叠。
	 *
	 * XXX 这太保守了吗？
	 */
	fc_listidx = -1;
	foreach(fc_lc, fc_clauses)
	{
		Selectivity fc_s2;

		fc_listidx++;

		/*
		 * 如果此子句已被上述某些其他统计估计，则跳过此子句。
		 */
		if (bms_is_member(fc_listidx, fc_estimatedclauses))
			continue;

		fc_s2 = clause_selectivity_ext(fc_root, (Node *) lfirst(fc_lc), fc_varRelid,
									fc_jointype, fc_sjinfo, fc_use_extended_stats);

		fc_s1 = fc_s1 + fc_s2 - fc_s1 * fc_s2;
	}

	return fc_s1;
}

/*
 * addRangeClause --- 为clauselist_selectivity添加一个新的范围子句
 *
 * 在这里我们尝试匹配一对范围查询子句
 */
static void fc_addRangeClause(RangeQueryClause **fc_rqlist, Node *fc_clause,
			   bool fc_varonleft, bool fc_isLTsel, Selectivity fc_s2)
{
	RangeQueryClause *fc_rqelem;
	Node	   *fc_var;
	bool		fc_is_lobound;

	if (fc_varonleft)
	{
		fc_var = get_leftop((Expr *) fc_clause);
		fc_is_lobound = !fc_isLTsel;	/* x < something是上界 */
	}
	else
	{
		fc_var = get_rightop((Expr *) fc_clause);
		fc_is_lobound = fc_isLTsel;	/* something < x是下界 */
	}

	for (fc_rqelem = *fc_rqlist; fc_rqelem; fc_rqelem = fc_rqelem->next)
	{
		/*
		 * 我们在这里使用完全equal()，因为“var”可能是
		 * 一个或多个相同关系的属性的函数……
		 */
		if (!equal(fc_var, fc_rqelem->var))
			continue;
		/* 找到了放置这个子句的正确组 */
		if (fc_is_lobound)
		{
			if (!fc_rqelem->have_lobound)
			{
				fc_rqelem->have_lobound = true;
				fc_rqelem->lobound = fc_s2;
			}
			else
			{

				/* ------
				 * 我们找到了两个相似的子句，例如
				 * x < y 和 x <= z。
				 * 只保留更具限制性的一个。
				 * ------
				 */
				if (fc_rqelem->lobound > fc_s2)
					fc_rqelem->lobound = fc_s2;
			}
		}
		else
		{
			if (!fc_rqelem->have_hibound)
			{
				fc_rqelem->have_hibound = true;
				fc_rqelem->hibound = fc_s2;
			}
			else
			{

				/* ------
				 * 我们找到了两个相似的子句，例如
				 * x > y 和 x >= z。
				 * 只保留更具限制性的一个。
				 * ------
				 */
				if (fc_rqelem->hibound > fc_s2)
					fc_rqelem->hibound = fc_s2;
			}
		}
		return;
	}

	/* 没有找到匹配的变量，因此创建一个新的子句对数据结构 */
	fc_rqelem = (RangeQueryClause *) palloc(sizeof(RangeQueryClause));
	fc_rqelem->var = fc_var;
	if (fc_is_lobound)
	{
		fc_rqelem->have_lobound = true;
		fc_rqelem->have_hibound = false;
		fc_rqelem->lobound = fc_s2;
	}
	else
	{
		fc_rqelem->have_lobound = false;
		fc_rqelem->have_hibound = true;
		fc_rqelem->hibound = fc_s2;
	}
	fc_rqelem->next = *fc_rqlist;
	*fc_rqlist = fc_rqelem;
}

/*
 * find_single_rel_for_clauses
 *		检查“clauses”中的每个子句，并确定所有子句
 *		是否仅引用单个关系。如果是，则返回该关系，
 *		否则返回NULL。
 */
static RelOptInfo * fc_find_single_rel_for_clauses(PlannerInfo *fc_root, List *fc_clauses)
{
	int			fc_lastrelid = 0;
	ListCell   *fc_l;

	foreach(fc_l, fc_clauses)
	{
		RestrictInfo *fc_rinfo = (RestrictInfo *) lfirst(fc_l);
		int			fc_relid;

		/*
		 * 如果我们有一组简单的子句而不是RestrictInfos，我们
		 * 可以用pull_varnos()硬性提取它们的relids。
		 * 然而，目前扩展统计机制不会对非RestrictInfo
		 * 子句做任何事情，所以没有必要耗费额外的周期；
		 * 如果我们有这种情况就失败。
		 *
		 * 这一规则的例外是如果我们有一个简单的BoolExpr AND
		 * 子句。我们将其视为特例，因为restrictinfo机制
		 * 不会在AND子句之上构建RestrictInfos。
		 */
		if (is_andclause(fc_rinfo))
		{
			RelOptInfo *fc_rel;

			fc_rel = fc_find_single_rel_for_clauses(fc_root,
											  ((BoolExpr *) fc_rinfo)->args);

			if (fc_rel == NULL)
				return NULL;
			if (fc_lastrelid == 0)
				fc_lastrelid = fc_rel->relid;
			else if (fc_rel->relid != fc_lastrelid)
				return NULL;

			continue;
		}

		if (!IsA(fc_rinfo, RestrictInfo))
			return NULL;

		if (bms_is_empty(fc_rinfo->clause_relids))
			continue;			/* 我们可以忽略无变量的子句 */
		if (!bms_get_singleton_member(fc_rinfo->clause_relids, &fc_relid))
			return NULL;		/* 此子句中有多个关系 */
		if (fc_lastrelid == 0)
			fc_lastrelid = fc_relid;	/* 第一个子句引用一个关系 */
		else if (fc_relid != fc_lastrelid)
			return NULL;		/* 关系与最后一个不同 */
	}

	if (fc_lastrelid != 0)
		return find_base_rel(fc_root, fc_lastrelid);

	return NULL;				/* 没有子句 */
}

/*
 * bms_is_subset_singleton
 *
 * 与bms_is_subset(s, bms_make_singleton(x))结果相同，
 * 但速度更快，并且不会泄漏内存。
 *
 * 这在其他地方有用吗？如果是，则移至bitmapset.c ...
 */
static bool fc_bms_is_subset_singleton(const Bitmapset *fc_s, int fc_x)
{
	switch (bms_membership(fc_s))
	{
		case BMS_EMPTY_SET:
			return true;
		case BMS_SINGLETON:
			return bms_is_member(fc_x, fc_s);
		case BMS_MULTIPLE:
			return false;
	}
	/* 不能到达这里... */
	return false;
}

/*
 * treat_as_join_clause -
 *	  决定一个操作符子句是通过限制或连接估算器来处理。 clause_selectivity() 的子程序。
 */
static inline bool fc_treat_as_join_clause(PlannerInfo *fc_root, Node *fc_clause, RestrictInfo *fc_rinfo,
					 int fc_varRelid, SpecialJoinInfo *fc_sjinfo)
{
	if (fc_varRelid != 0)
	{
		/*
		 * 调用者正在强制限制模式（例如，因为我们正在检查一个
		 * 内部索引扫描条件）。
		 */
		return false;
	}
	else if (fc_sjinfo == NULL)
	{
		/*
		 * 它必须是一个限制子句，因为它是在扫描节点上评估的。
		 */
		return false;
	}
	else
	{
		/*
		 * 否则，如果使用了多个关系，则它是一个连接。如果传递了
		 * rinfo，我们可以优化此计算。
		 *
		 * XXX 由于我们知道该子句是在连接中评估的，它
		 * 唯一可能是单个关系的情况是它被外连接延迟了。尽管我们无论如何
		 * 都可以利用限制条件估计器，但似乎我们应该以某种方式考虑
		 * 注入空值的概率。
		 */
		if (fc_rinfo)
			return (bms_membership(fc_rinfo->clause_relids) == BMS_MULTIPLE);
		else
			return (NumRelids(fc_root, fc_clause) > 1);
	}
}


/*
 * clause_selectivity -
 *	  计算一般布尔表达式子句的选择性。
 *
 * 子句可以是 RestrictInfo 或普通表达式。如果它是
 * RestrictInfo，我们尝试缓存选择性以便可能重用，
 * 因此首选传递 RestrictInfos。
 *
 * varRelid 要么是 0，要么是 rangetable 索引。
 *
 * 当 varRelid 不是 0 时，仅考虑属于该关系的变量
 * 来计算选择性；其他变量被视为未知值的常量。
 * 这适用于估算在嵌套循环连接的内关系的扫描中作为限制子句使用的
 * 连接子句的选择性——此时 varRelid 应该是
 * 内部关系的 ID。
 *
 * 当 varRelid 为 0 时，所有变量都被视为变量。
 * 这适用于普通连接子句和限制子句。
 *
 * jointype 是连接类型，如果子句是连接子句。如果子句
 * 不是连接子句，则传递 JOIN_INNER。
 *
 * 对于非连接子句，sjinfo 为 NULL，否则它提供有关
 * 正在执行的连接的额外上下文信息。有一些
 * 特殊情况：
 *	1. 对于特殊（非内部）连接，sjinfo 始终是
 *	   root->join_info_list 的成员。
 *	2. 对于内部连接，sjinfo 只是一个临时结构，并且仅
 *	   relids 和 jointype 字段可以信任。
 * jointype 可能与 sjinfo->jointype 不同。
 * 这表示我们正在考虑一个变体连接：要么是
 * LHS 和 RHS 交换，或一个输入被唯一化。
 *
 * 注意：在传递非零 varRelid 时，通常适合将
 * jointype 设置为 JOIN_INNER，sjinfo 设置为 NULL，
 * 即使子句实际上是一个连接子句；因为我们没有将其视为连接子句。
 */
Selectivity clause_selectivity(PlannerInfo *fc_root,
				   Node *fc_clause,
				   int fc_varRelid,
				   JoinType fc_jointype,
				   SpecialJoinInfo *fc_sjinfo)
{
	return clause_selectivity_ext(fc_root, fc_clause, fc_varRelid,
								  fc_jointype, fc_sjinfo, true);
}

/*
 * clause_selectivity_ext -
 *	  clause_selectivity() 的扩展版本。如果 "use_extended_stats" 为
 *	  false，所有扩展统计信息将被忽略，仅使用每列
 *	  统计信息。
 */
Selectivity clause_selectivity_ext(PlannerInfo *fc_root,
					   Node *fc_clause,
					   int fc_varRelid,
					   JoinType fc_jointype,
					   SpecialJoinInfo *fc_sjinfo,
					   bool fc_use_extended_stats)
{
	Selectivity fc_s1 = 0.5;		/* 对于任何未处理的子句类型的默认值 */
	RestrictInfo *fc_rinfo = NULL;
	bool		fc_cacheable = false;

	if (fc_clause == NULL)			/* 这还能发生吗？ */
		return fc_s1;

	if (IsA(fc_clause, RestrictInfo))
	{
		fc_rinfo = (RestrictInfo *) fc_clause;

		/*
		 * 如果子句标记为伪常量，则将其用作门控条件，不应影响选择性估计；
		 * 因此返回 1.0。唯一的例外是常量 FALSE 可以被
		 * 视为选择性 0.0，因为这肯定意味着计划中没有行。
		 * 这个情况足够简单，我们不需要麻烦缓存结果。
		 */
		if (fc_rinfo->pseudoconstant)
		{
			if (!IsA(fc_rinfo->clause, Const))
				return (Selectivity) 1.0;
		}

		/*
		 * 如果子句标记为冗余，始终返回 1.0。
		 */
		if (fc_rinfo->norm_selec > 1)
			return (Selectivity) 1.0;

		/*
		 * 如果可能，缓存该子句选择性计算的结果。
		 * 如果 varRelid 为零或子句仅包含该 relid 的变量，我们可以缓存；
		 * 否则 varRelid 将影响结果，因此不能缓存。
		 * 外连接条件可能会以其连接的实际连接类型或 JOIN_INNER 进行检查，
		 * 因此我们需要两个缓存变量来记住这两种情况。
		 * 注意：我们假设如果我们正在切换输入关系或
		 * 考虑唯一化情况，结果不会改变，因此我们只需要一个缓存变量
		 * 对于所有非 JOIN_INNER 的情况。
		 */
		if (fc_varRelid == 0 ||
			fc_bms_is_subset_singleton(fc_rinfo->clause_relids, fc_varRelid))
		{
			/* 可缓存——我们是否已经有结果？ */
			if (fc_jointype == JOIN_INNER)
			{
				if (fc_rinfo->norm_selec >= 0)
					return fc_rinfo->norm_selec;
			}
			else
			{
				if (fc_rinfo->outer_selec >= 0)
					return fc_rinfo->outer_selec;
			}
			fc_cacheable = true;
		}

		/*
		 * 继续检查包含的子句。如果子句是一个
		 * OR 子句，我们想要查看带有子 RestrictInfos 的变体，
		 * 以便缓存每个子子句的选择性。
		 */
		if (fc_rinfo->orclause)
			fc_clause = (Node *) fc_rinfo->orclause;
		else
			fc_clause = (Node *) fc_rinfo->clause;
	}

	if (IsA(fc_clause, Var))
	{
		Var		   *fc_var = (Var *) fc_clause;

		/*
		 * 我们可能不应该在这里看到任何上层 Var，但如果我们这样做，
		 * 则返回默认选择性...
		 */
		if (fc_var->varlevelsup == 0 &&
			(fc_varRelid == 0 || fc_varRelid == (int) fc_var->varno))
		{
			/* 对于 bool Var 使用限制选择性函数 */
			fc_s1 = boolvarsel(fc_root, (Node *) fc_var, fc_varRelid);
		}
	}
	else if (IsA(fc_clause, Const))
	{
		/* bool 常量相当简单... */
		Const	   *fc_con = (Const *) fc_clause;

		fc_s1 = fc_con->constisnull ? 0.0 :
			DatumGetBool(fc_con->constvalue) ? 1.0 : 0.0;
	}
	else if (IsA(fc_clause, Param))
	{
		/* 查看我们是否可以替换 Param */
		Node	   *fc_subst = estimate_expression_value(fc_root, fc_clause);

		if (IsA(fc_subst, Const))
		{
			/* bool 常量相当简单... */
			Const	   *fc_con = (Const *) fc_subst;

			fc_s1 = fc_con->constisnull ? 0.0 :
				DatumGetBool(fc_con->constvalue) ? 1.0 : 0.0;
		}
		else
		{
			/* XXX 有没有比默认更好的方法？ */
		}
	}
	else if (is_notclause(fc_clause))
	{
		/* 基础子句选择性的倒数 */
		fc_s1 = 1.0 - clause_selectivity_ext(fc_root,
										  (Node *) get_notclausearg((Expr *) fc_clause),
										  fc_varRelid,
										  fc_jointype,
										  fc_sjinfo,
										  fc_use_extended_stats);
	}
	else if (is_andclause(fc_clause))
	{
		/* 与 clauselist_selectivity() 共享代码 */
		fc_s1 = clauselist_selectivity_ext(fc_root,
										((BoolExpr *) fc_clause)->args,
										fc_varRelid,
										fc_jointype,
										fc_sjinfo,
										fc_use_extended_stats);
	}
	else if (is_orclause(fc_clause))
	{
		/*
		 * 与 clauselist_selectivity 几乎相同，但子句通过 OR 连接。
		 */
		fc_s1 = fc_clauselist_selectivity_or(fc_root,
									   ((BoolExpr *) fc_clause)->args,
									   fc_varRelid,
									   fc_jointype,
									   fc_sjinfo,
									   fc_use_extended_stats);
	}
	else if (is_opclause(fc_clause) || IsA(fc_clause, DistinctExpr))
	{
		OpExpr	   *fc_opclause = (OpExpr *) fc_clause;
		Oid			fc_opno = fc_opclause->opno;

		if (fc_treat_as_join_clause(fc_root, fc_clause, fc_rinfo, fc_varRelid, fc_sjinfo))
		{
			/* 估算连接子句的选择性。 */
			fc_s1 = join_selectivity(fc_root, fc_opno,
								  fc_opclause->args,
								  fc_opclause->inputcollid,
								  fc_jointype,
								  fc_sjinfo);
		}
		else
		{
			/* 估算限制子句的选择性。 */
			fc_s1 = restriction_selectivity(fc_root, fc_opno,
										 fc_opclause->args,
										 fc_opclause->inputcollid,
										 fc_varRelid);
		}

		
/*
		 * DistinctExpr的表示与OpExpr相同，但包含的运算符是"="而不是"<>"，因此我们必须对结果取反。
		 * 此估算方法对null的行为没有给出正确的结果，但总比什么都不做要好。
		 */
		if (IsA(fc_clause, DistinctExpr))
			fc_s1 = 1.0 - fc_s1;
	}
	else if (is_funcclause(fc_clause))
	{
		FuncExpr   *fc_funcclause = (FuncExpr *) fc_clause;

		/* 尝试从支持函数中获取估算，如果有的话 */
		fc_s1 = function_selectivity(fc_root,
								  fc_funcclause->funcid,
								  fc_funcclause->args,
								  fc_funcclause->inputcollid,
								  fc_treat_as_join_clause(fc_root, fc_clause, fc_rinfo,
													   fc_varRelid, fc_sjinfo),
								  fc_varRelid,
								  fc_jointype,
								  fc_sjinfo);
	}
	else if (IsA(fc_clause, ScalarArrayOpExpr))
	{
		/* 使用节点特定的选择性计算函数 */
		fc_s1 = scalararraysel(fc_root,
							(ScalarArrayOpExpr *) fc_clause,
							fc_treat_as_join_clause(fc_root, fc_clause, fc_rinfo,
												 fc_varRelid, fc_sjinfo),
							fc_varRelid,
							fc_jointype,
							fc_sjinfo);
	}
	else if (IsA(fc_clause, RowCompareExpr))
	{
		/* 使用节点特定的选择性计算函数 */
		fc_s1 = rowcomparesel(fc_root,
						   (RowCompareExpr *) fc_clause,
						   fc_varRelid,
						   fc_jointype,
						   fc_sjinfo);
	}
	else if (IsA(fc_clause, NullTest))
	{
		/* 使用节点特定的选择性计算函数 */
		fc_s1 = nulltestsel(fc_root,
						 ((NullTest *) fc_clause)->nulltesttype,
						 (Node *) ((NullTest *) fc_clause)->arg,
						 fc_varRelid,
						 fc_jointype,
						 fc_sjinfo);
	}
	else if (IsA(fc_clause, BooleanTest))
	{
		/* 使用节点特定的选择性计算函数 */
		fc_s1 = booltestsel(fc_root,
						 ((BooleanTest *) fc_clause)->booltesttype,
						 (Node *) ((BooleanTest *) fc_clause)->arg,
						 fc_varRelid,
						 fc_jointype,
						 fc_sjinfo);
	}
	else if (IsA(fc_clause, CurrentOfExpr))
	{
		/* CURRENT OF至多选择其表中的一行 */
		CurrentOfExpr *fc_cexpr = (CurrentOfExpr *) fc_clause;
		RelOptInfo *fc_crel = find_base_rel(fc_root, fc_cexpr->cvarno);

		if (fc_crel->tuples > 0)
			fc_s1 = 1.0 / fc_crel->tuples;
	}
	else if (IsA(fc_clause, RelabelType))
	{
		/* 不确定这个情况是否需要，但不会有坏处 */
		fc_s1 = clause_selectivity_ext(fc_root,
									(Node *) ((RelabelType *) fc_clause)->arg,
									fc_varRelid,
									fc_jointype,
									fc_sjinfo,
									fc_use_extended_stats);
	}
	else if (IsA(fc_clause, CoerceToDomain))
	{
		/* 不确定这个情况是否需要，但不会有坏处 */
		fc_s1 = clause_selectivity_ext(fc_root,
									(Node *) ((CoerceToDomain *) fc_clause)->arg,
									fc_varRelid,
									fc_jointype,
									fc_sjinfo,
									fc_use_extended_stats);
	}
	else
	{
		/*
		 * 对于其他任何情况，看看我们是否可以将其视为布尔变量。
		 * 这仅在它是单一关系的Vars中的不可变表达式时有效；但在这里检查没有意义，因为
		 * boolvarsel()会在内部处理，并在没有时返回一个合适的默认选择性。
		 */
		fc_s1 = boolvarsel(fc_root, fc_clause, fc_varRelid);
	}

	/* 如果可能，缓存结果 */
	if (fc_cacheable)
	{
		if (fc_jointype == JOIN_INNER)
			fc_rinfo->norm_selec = fc_s1;
		else
			fc_rinfo->outer_selec = fc_s1;
	}

#ifdef SELECTIVITY_DEBUG
	elog(DEBUG4, "clause_selectivity: s1 %f", fc_s1);
#endif							/* SELECTIVITY_DEBUG */

	return fc_s1;
}
